use crate::typedef::ToAddr;
use bimap::btree::BiBTreeMap;
use enum_dispatch::enum_dispatch;
use rand::Rng;
use std::{borrow::Borrow, cmp::Ord, sync::atomic::AtomicU32};

macro_rules! ord {
  ($cls:ident $key:ident : $key_cls:ident) => {
    impl<Addr: ToAddr> PartialEq for $cls<Addr> {
      fn eq(&self, other: &Self) -> bool {
        self.$key == other.$key
      }
    }

    impl<Addr: ToAddr> PartialOrd for $cls<Addr> {
      fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.$key.partial_cmp(&other.$key)
      }
    }

    impl<Addr: ToAddr> Eq for $cls<Addr> {}
    impl<Addr: ToAddr> Ord for $cls<Addr> {
      fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.$key.cmp(&other.$key)
      }
    }

    impl<Addr: ToAddr> Borrow<$key_cls> for $cls<Addr> {
      fn borrow(&self) -> &$key_cls {
        &self.$key
      }
    }
  };
}

#[enum_dispatch]
pub trait Recv {
  fn recv(&self, msg: &[u8]);
}

#[enum_dispatch(Recv)]
#[derive(Debug)]
pub enum Task<Addr: ToAddr> {
  Kad(Kad<Addr>),
}

#[derive(Debug)]
pub struct Kad<Addr: ToAddr> {
  addr: Addr,
}

impl<Addr: ToAddr> Recv for Kad<Addr> {
  fn recv(&self, msg: &[u8]) {}
}

#[derive(Debug)]
pub struct IdTask<Addr: ToAddr> {
  pub id: u32,
  pub task: Task<Addr>,
}

ord!(IdTask id: u32);

#[derive(Debug)]
pub struct AddrTask<Addr: ToAddr> {
  pub addr: Addr,
  pub time_task: BiBTreeMap<u64, IdTask<Addr>>,
  pub id: AtomicU32,
}

ord!(AddrTask addr: Addr);

#[derive(Debug)]
pub struct TimeAddr<Addr: ToAddr> {
  pub time_addr: BiBTreeMap<u64, AddrTask<Addr>>,
}

impl<Addr: ToAddr> Default for TimeAddr<Addr> {
  fn default() -> Self {
    TimeAddr {
      time_addr: BiBTreeMap::new(),
    }
  }
}

impl<Addr: ToAddr> TimeAddr<Addr> {
  fn addr_task_id(&mut self, addr: &Addr, task: Task<Addr>) -> u32 {
    let id: u32 = rand::thread_rng().gen();
    dbg!(id);
    id
  }
}
